module sccb (
    input clk,
    input rst_n,

    output scl,
    inout sda,
    input start_signal,
    input [7:0] wdata,
    output ld_wdata,
    input [7:0] wdata_len,
    output ack_error,
    output dev_ack_error,

    output [7:0] rdata,
    output rdata_vaild,
    input [7:0] rdata_len,

    input [7:0] dev_addr,
    output transfer_finish,
    input sccb_stop_signal,
    output handle

);
parameter CLK_FREQ=5000000;	//系统时钟频率
parameter SCCB_CLK=9600;		//波特率
localparam SCCB_CNT = (CLK_FREQ*3/SCCB_CLK -1);

reg [7:0] wdata_r;
reg ld_wdata_r;
reg ack_error_r;
reg dev_ack_error_r;
reg rdata_vaild_r;
reg transfer_finish_r;
assign transfer_finish = transfer_finish_r;

assign ld_wdata = ld_wdata_r;
assign ack_error = ack_error_r;
assign dev_ack_error = dev_ack_error_r;
assign rdata_vaild = rdata_vaild_r;

reg [7:0] rdata_r;
reg [7:0] byte_cnt;
reg [31:0] tick_cnt;

assign rdata = rdata_r;

localparam SCCB_IDLE    =   'd0;
localparam SCCB_START   =   'd1;
localparam SCCB_DEV     =   'd2;
localparam SCCB_STOP    =   'd3;
localparam SCCB_WRITE   =   'd4;
localparam SCCB_READ    =   'd5;

reg [3:0] SCCB_STATE;
reg [7:0] dev_addr_r;
reg handle_r;
assign handle = handle_r;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tick_cnt <= 'd0;
    end
    else begin
        if(SCCB_STATE != SCCB_IDLE)begin
            if(tick_cnt < SCCB_CNT) begin
                tick_cnt <= tick_cnt + 'b1;
            end
            else begin
                tick_cnt <= 'd0;
            end
        end
        else begin
            tick_cnt <= 'd0;
        end
    end
end

reg sda_oe;
reg sda_o;
wire sda_i;
reg scl_r;
assign sda = (sda_oe == 'b1) ? sda_o : 'bz;
assign sda_i = sda;
assign scl = scl_r;
reg [2:0] seg;
reg [3:0] bit_cnt;
reg [7:0] shift_byte;
reg start_signal_r;
reg sccb_stop_signal_r;
always @(posedge clk ) begin
    start_signal_r <= start_signal;
    sccb_stop_signal_r <= sccb_stop_signal;
end
wire start_signal_pos;
wire stop_signal_pos;
assign start_signal_pos = (~start_signal_r)&&start_signal;
assign stop_signal_pos = (~sccb_stop_signal_r)&&sccb_stop_signal;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sda_oe <= 'b1;
        sda_o <= 'b1;
        scl_r <= 'b1;
        SCCB_STATE <= SCCB_IDLE;
        seg <= 'd0;
        dev_ack_error_r <= 'b0;
        ld_wdata_r <= 'b0;
        ack_error_r <= 'b1;
        handle_r <= 'b0;
    end
    else begin
        case(SCCB_STATE)
        SCCB_IDLE:begin
            handle_r <= 'b0;
            transfer_finish_r <= 'b0;
            ld_wdata_r <= 'b0;
            rdata_vaild_r <= 'b0;
            ack_error_r <= 'b0;
            byte_cnt <= 'd0;
            bit_cnt <= 'd0;
            sda_oe <= sda_oe;
            sda_o <= sda_o;
            scl_r <= scl_r;
            dev_ack_error_r <= 'b0;
            SCCB_STATE <= SCCB_IDLE;
            seg <= 'd0;
            if(start_signal_pos) begin
                handle_r <= 'b1;
                dev_addr_r <= dev_addr;
                SCCB_STATE <= SCCB_START;
                seg <= 'd0;
            end
            else if(stop_signal_pos)begin
                seg <= 'd0;
                SCCB_STATE <= SCCB_STOP;
                handle_r <= 'b1;
            end
        end
        SCCB_START:begin
            handle_r <= handle_r;
            transfer_finish_r <= transfer_finish_r;
            ld_wdata_r <= 'b0;
            ack_error_r <= 'b0;
            dev_ack_error_r <= 'b0;
            SCCB_STATE <= SCCB_STATE;
            dev_addr_r <= dev_addr_r;
            scl_r <= scl_r;
            sda_oe <= sda_oe;
            sda_o <= sda_o;
            if(tick_cnt == SCCB_CNT) begin
                seg <= seg + 'b1;
                case(seg)
                'd0:begin
                    if(scl_r) begin
                        sda_oe <= 'b1;
                        sda_o <= 'b0;
                        seg <= 'd4;
                    end
                    else begin
                        scl_r <= 'b0;
                    end
                end
                'd1:begin
                    sda_oe  <= 'b1;
                    sda_o   <= 'b1;
                end
                'd2:begin
                    scl_r <= 'b1;
                end
                'd3:begin
                    sda_o <= 'b0;
                end
                'd4:begin
                    scl_r <= 'b0;
                    seg <='d0;
                    SCCB_STATE <= SCCB_DEV;
                    shift_byte <= dev_addr_r;
                    byte_cnt <= 'd0;
                    bit_cnt <= 'd0;
                end
                endcase
            end
            else begin
                seg <= seg;
            end
            
        end
        SCCB_DEV:begin
            handle_r <= handle_r;
            transfer_finish_r <= transfer_finish_r;
            ld_wdata_r <= 'b0;
            ack_error_r <= 'b0;
            dev_ack_error_r <= 'b0;
            SCCB_STATE <= SCCB_DEV;
            sda_oe <= sda_oe;
            sda_o <= sda_o;
            scl_r <= scl_r;
            bit_cnt <= bit_cnt;
            if(tick_cnt == SCCB_CNT) begin
                seg <= seg + 'b1;
                case(seg)
                'd0:begin
                    scl_r <= 'b0;
                    sda_o <= shift_byte[7];
                end
                'd1:begin
                    scl_r <= 'b1;
                end
                'd2:begin
                    scl_r <= 'b0;
                    seg <= 'd0;
                    shift_byte <= {shift_byte[6:0],1'b0};
                    bit_cnt <= bit_cnt + 1'b1;
                    if(bit_cnt == 'd7) begin
                        sda_oe <= 'b0;
                    end
                    else if(bit_cnt == 'd8) begin
                        if(sda_i != 'b0) begin
                            dev_ack_error_r <= 'b1;
                        end
                        bit_cnt <= 'd0;
                        byte_cnt <= 'd1;
                        if(dev_addr[0]) begin
                            SCCB_STATE <= SCCB_READ;
                            sda_oe <= 'b0;
                        end
                        else begin
                            sda_oe <= 'b1;
                            shift_byte <= wdata;
                            ld_wdata_r <= 'b1;//提示上层准备下一数据
                            SCCB_STATE <= SCCB_WRITE;
                        end
                    end
                end
                endcase
            end
            else begin
                seg <= seg;
            end
            
        end
        SCCB_READ:begin
            handle_r <= handle_r;
            transfer_finish_r <= transfer_finish_r;
            ld_wdata_r <= 'b0;
            ack_error_r <= 'b0;
            dev_ack_error_r <= 'b0;
            SCCB_STATE <= SCCB_READ;
            sda_oe <= sda_oe;
            sda_o <= sda_o;
            scl_r <= scl_r;
            bit_cnt <= bit_cnt;
            rdata_r <= rdata_r;
            rdata_vaild_r <= 'b0;
            byte_cnt <= byte_cnt;
            shift_byte <= shift_byte;
            
            if(tick_cnt == SCCB_CNT) begin
                seg <= seg + 'b1;
                case(seg)
                'd0:begin
                    scl_r <= 'b0;
                    if(bit_cnt == 'd8)begin
                        sda_oe <= 'b1;
                        if(byte_cnt == rdata_len) begin
                            sda_o <= 1'b1;
                        end
                        else begin
                            sda_o <= 1'b0;
                        end
                    end
                    else begin
                        sda_oe <= 'b0;
                    end
                end
                'd1:begin
                    scl_r <= 'b1;
                end
                'd2:begin
                    seg <= 'd0;
                    scl_r <= 'b0;
                    if(bit_cnt <= 'd7) begin
                        bit_cnt <= bit_cnt + 'b1;
                        shift_byte <= {shift_byte[6:0],sda_i};
                        if(bit_cnt == 'd7) begin
                            sda_oe <= 1'b1;
                        end
                        else begin
                            sda_oe <= 1'b0;
                        end
                    end
                    else if(bit_cnt == 'd8) begin
                        bit_cnt <= 'd0;
                        rdata_vaild_r <= 'b1;
                        rdata_r <=  shift_byte;
                        shift_byte <= 'd00;
                        byte_cnt <= byte_cnt + 'd1;
                        if(byte_cnt == rdata_len)begin
                            SCCB_STATE <= SCCB_IDLE;
                            transfer_finish_r <= 'b1;
                        end
                    end
                    else begin
                        
                    end
                end
                endcase
            end
            else begin
                seg <= seg;
            end
            
        end
        SCCB_WRITE:begin
            handle_r <= handle_r;
            transfer_finish_r <= transfer_finish_r;
            ld_wdata_r <= 'b0;
            ack_error_r <= 'b0;
            dev_ack_error_r <= 'b0;
            SCCB_STATE <= SCCB_WRITE;
            sda_oe <= sda_oe;
            sda_o <= sda_o;
            scl_r <= scl_r;
            bit_cnt <= bit_cnt;
            rdata_r <= rdata_r;
            rdata_vaild_r <= 'b0;
            byte_cnt <= byte_cnt;
            shift_byte <= shift_byte;
            
            if(tick_cnt == SCCB_CNT) begin
                seg <= seg + 'b1;
                case(seg)
                'd0:begin
                    scl_r <= 'b0;
                    sda_o <= shift_byte[7];
                end
                'd1:begin
                    scl_r <= 'b1;
                end
                'd2:begin
                    shift_byte <= {shift_byte[6:0],1'b1};
                    seg <= 'd0;
                    scl_r <= 'b0;
                    if(bit_cnt == 'd8) begin
                        if(sda_i != 'b0)begin
                            ack_error_r <= 'b1;
                        end
                        byte_cnt <= byte_cnt + 1'b1;
                        if(byte_cnt == wdata_len)begin
                            SCCB_STATE <= SCCB_IDLE;
                            transfer_finish_r <= 'b1;
                        end
                        else begin
                            shift_byte <= wdata;
                            ld_wdata_r <= 'b1;
                        end
                        bit_cnt <= 'd0;
                    end
                    else begin
                        bit_cnt <= bit_cnt + 1'b1;
                    end
                    if(bit_cnt == 'd7) begin
                        sda_oe <= 'b0;
                    end
                    else begin
                        sda_oe <= 'b1;
                    end
                end
                endcase
            end
            else begin
                seg <= seg;
            end
            
        end
        SCCB_STOP:begin
            handle_r <= handle_r;
            scl_r <= scl_r;
            sda_oe <= sda_oe;
            sda_o <= sda_o;
            SCCB_STATE <= SCCB_STATE;
            
            if(tick_cnt == SCCB_CNT) begin
                seg <= seg + 'b1;
                case(seg)
                'd0:begin
                    scl_r <= 1'b0;
                    sda_oe <= 1'b1;
                end
                'd1:begin
                    sda_o <= 'b0;
                end
                'd2:begin
                    scl_r <= 'b1;
                end
                'd3:begin
                    sda_o <= 'b1;
                end
                'd4:begin
                    transfer_finish_r <= 'b1;
                    SCCB_STATE <= SCCB_IDLE;
                end
                endcase
            end
            else begin
                seg <= seg;
            end
            
        end
        endcase
    end
end
endmodule